/* free_and_sbrk.c

   Test if free(3) actually lowers the program break.

   Usage: free_and_sbrk num-allocs block-size [step [min [max]]]

   Try: free_and_sbrk 1000 10240 2 1 1000
        free_and_sbrk 1000 10240 1 1 999
        free_and_sbrk 1000 10240 1 500 1000

        (Only the last of these should see the program break lowered.)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MAX_ALLOCS 1000000

int main(int argc, char *argv[]) {
  char *ptr[MAX_ALLOCS];
  int freeStep, freeMin, freeMax, blockSize, numAllocs, j;

  printf("\n");

  if (argc < 3 || strcmp(argv[1], "--help") == 0) {
    fprintf(stderr, "%s num-allocs block-size [step [min [max]]]\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  numAllocs = strtol(argv[1], NULL, 10);
  if (numAllocs > MAX_ALLOCS) {
    fprintf(stderr, "num-allocs > %d\n", MAX_ALLOCS);
    exit(EXIT_FAILURE);
  }

  blockSize = strtol(argv[2], NULL, 10);

  freeStep = (argc > 3) ? strtol(argv[3], NULL, 10) : 1;
  freeMin = (argc > 4) ? strtol(argv[4], NULL, 10) : 1;
  freeMax = (argc > 5) ? strtol(argv[5], NULL, 10) : numAllocs;

  if (freeMax > numAllocs) {
    fprintf(stderr, "free-max > num-allocs\n");
    exit(EXIT_FAILURE);
  }

  printf("Initial program break:          %10p\n", sbrk(0));

  printf("Allocating %d*%d bytes\n", numAllocs, blockSize);
  for (j = 0; j < numAllocs; j++) {
    ptr[j] = malloc(blockSize);
    if (ptr[j] == NULL) {
      perror("malloc");
      exit(EXIT_FAILURE);
    }
  }

  printf("Program break is now:           %10p\n", sbrk(0));

  printf("Freeing blocks from %d to %d in steps of %d\n", freeMin, freeMax,
         freeStep);
  for (j = freeMin - 1; j < freeMax; j += freeStep) free(ptr[j]);

  printf("After free(), program break is: %10p\n", sbrk(0));

  exit(EXIT_SUCCESS);
}